package respository

import (
	myerror "CodeCover/internal/errors"
	"CodeCover/internal/models"
	"CodeCover/internal/utils"
	"context"
	"errors"

	"gorm.io/gorm"
)

type MysqlUserRepository struct {
	db *gorm.DB
}

func NewMysqlUserRepository(db *gorm.DB) *MysqlUserRepository {
	db.AutoMigrate(&models.User{})
	return &MysqlUserRepository{db: db}
}

// create a new user
func (r *MysqlUserRepository) Create(ctx context.Context, user *models.User) error {
	if ret := r.db.WithContext(ctx).Create(user); ret.Error != nil {

		return &myerror.CustomError{
			Code:        myerror.DefaultFailure,
			Message:     "MysqlUserRepository:Create() failed," + ret.Error.Error(),
			OriginError: ret.Error,
			Metadata: map[string]any{
				"username": user.UserName,
			},
		}

	}
	return nil
}

// find a existed user by name
func (r *MysqlUserRepository) FindByName(ctx context.Context, username string) (*models.User, error) {
	var user models.User
	if ret := r.db.WithContext(ctx).Where("user_name = ?", username).First(&user); ret.Error != nil {
		var code myerror.ErrorCode = myerror.DefaultFailure
		if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
			code = myerror.RecordNotFound
		}

		return nil, &myerror.CustomError{
			Code:        code,
			Message:     "MysqlUserRepository:FindByName() failed," + ret.Error.Error(),
			OriginError: ret.Error,
			Metadata: map[string]any{
				"username": user.UserName,
			},
		}
	}
	return &user, nil
}

func (r *MysqlUserRepository) GetUidByName(ctx context.Context, username string) (uint64, error) {
	var u models.User
	if ret := r.db.WithContext(ctx).Where("user_name = ?", username).First(&u); ret.Error != nil {
		var code myerror.ErrorCode = myerror.DefaultFailure
		if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
			code = myerror.RecordNotFound
		}

		return 0, &myerror.CustomError{
			Code:        code,
			Message:     "MysqlUserRepository:GetUidByName() failed," + ret.Error.Error(),
			OriginError: ret.Error,
			Metadata: map[string]any{
				"username": username,
			},
		}
	}
	return u.Uid, nil
}

func (r *MysqlUserRepository) ApplyToken(ctx context.Context, username string) (string, error) {
	token, err := utils.GenerateSecureRandomToken(24)
	if err != nil {
		return "", &myerror.CustomError{
			Code:        myerror.Unknown,
			Message:     "MysqlUserRepository:ApplyToken() failed," + err.Error(),
			OriginError: err,
			Metadata: map[string]any{
				"username": username,
			},
		}
	}

	if ret := r.db.WithContext(ctx).Model(&models.User{}).Where("user_name = ?", username).Update("token", token); ret.Error != nil {

		return "", &myerror.CustomError{
			Code:        myerror.Unknown,
			Message:     "MysqlUserRepository:ApplyToken() failed," + ret.Error.Error(),
			OriginError: err,
			Metadata: map[string]any{
				"username": username,
			},
		}
	}
	return token, nil

}

func (r *MysqlUserRepository) IsValidToken(ctx context.Context, username string, token string) (bool, error) {
	var u models.User
	if ret := r.db.WithContext(ctx).Where("user_name = ?", username).First(&u); ret.Error != nil {
		var code myerror.ErrorCode = myerror.DefaultFailure
		if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
			code = myerror.RecordNotFound
		}

		return false, &myerror.CustomError{
			Code:        code,
			Message:     "MysqlUserRepository:IsValidToken() failed," + ret.Error.Error(),
			OriginError: ret.Error,
			Metadata: map[string]any{
				"username": username,
			},
		}
	}

	return u.Token == token, nil
}
